import axios from 'axios'
import { Dialog } from 'quasar'
import ErrCode from 'src/assets/json/err'
import {
  loadRefreshToken,
  loadToken,
  removeRefreshToken,
  removeToken,
  saveRefreshToken,
  saveToken
} from 'yz-help'
import Vue from 'vue'
import createStore from 'app/src/store/index'
import jsonBigint from 'json-bigint'
import { goLoginPage } from '../permission'
import qs from 'qs'

const axiosInstance = axios.create({
  baseURL: process.env.API_BASE_URL,
  withCredentials: true,
  timeout: 0,
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  }
})

const ignorLink = ['/login', '/loginbycode', '/code', '/user_companies']

// 判断是否是忽略的连接
function isIgnorLinkFun (url) {
  let isIgnorLink = false
  for (let i = 0; i < ignorLink.length; i++) {
    if (url.indexOf(ignorLink[i]) > -1) {
      isIgnorLink = true
      break
    }
  }
  return isIgnorLink
}

// 获取store
function getStore () {
  let store = typeof createStore === 'function'
    ? createStore({ Vue })
    : createStore
  return store
}
// 获取token
function getToken (store, url) {
  let token
  if (store && store.getters.token) {
    token = store.getters.token
  } else {
    token = loadToken()
  }
  if (token.toLowerCase().indexOf('bearer') > -1) {
    return token
  }
  return 'bearer ' + token
}

axiosInstance.interceptors.request.use(
  config => {
    if (config.mock) {
      config.baseURL = config.mockUrl
    }
    const store = getStore()
    const url = config.url
    // 判断是否是忽略的连接
    if (isIgnorLinkFun(url)) {
      return config
    }
    // 不是忽略的连接，要加token
    let token = getToken(store, url)
    // 判断token是否存在
    if (token.length === 0 || token === undefined) {
      goLoginPage()
      return Promise.reject(false)
    }
    if (!config.headers.Authorization) {
      config.headers.Authorization = token
    }
    if (window.demonstrate) {
      config.headers['Ignore-Session'] = true
    }
    if (config.headers['Content-Type'] === 'application/x-www-form-urlencoded' && (config.method === 'post' || config.method === 'POST')) {
      config.data = qs.stringify(config.data)
    }

    if (config.headers['Content-Type'] === 'multipart/form-data' && (config.method === 'post' || config.method === 'POST' || config.method === 'put' || config.method === 'PUT')) {
      const form = new FormData()
      const params = config.data
      Object.keys(params).forEach((key) => {
        if (params[key]) {
          form.append(key, params[key])
        }
      })
      config.data = form
    }
    return config
  }, function (error) {
    return Promise.reject(error)
  }
)
// 自定义提示的错误码
const customTipCode = ['1682', '1696', '1697', '1709', '1710', '1714', '1718', '1723', '1724', '1725', '2021']
// 请求参数不合法，引用message的提示内容
const yzCode = ['1512']
axiosInstance.interceptors.response.use(
  response => {
    // 1200与自定义提示错误码
    if (response.data.code === '1200' || customTipCode.includes(response.data.code)) {
      const rawResponse = response.request.responseText
      return jsonBigint({ storeAsString: true }).parse(rawResponse)
    }
    // 以下状态码不弹出提示
    if (response.data.code === '1401' || response.data.code === '1572') {
      return response.data
    }

    // 若 Access token 过期，则续签
    if (response.data.code === '1402') {
      renewAccessToken()
      return pendRequest(response.config)
    }

    if (response.data.code === '1573') {
      removeToken()
      removeRefreshToken()
      return goLoginPage()
    }
    switch (response.data.code) {
      case '1403':
        Dialog.create({
          title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-52" ></i> 提示</p>`,
          message: ErrCode[response.data.code] + ',请重新登录',
          html: true,
          ok: {
            unelevated: true,
            label: '确认'
          }
        }).onOk(() => {
          goLoginPage()
        })
        break
      case '1500':
        let msg = `系统繁忙,请稍后再试`
        if (response.config.tOutCus && response.data.data.includes('java.lang.OutOfMemoryError')) {
          msg = `导出数据量过大, 请尝试缩小时间范围再进行导出操作`
        }
        Dialog.create({
          title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-52" ></i> 提示</p>`,
          // message: `code为1500，接口有错误了`,
          message: msg,
          html: true,
          ok: {
            unelevated: true,
            label: '确认'
          }
        })
        break
      case '1601':
      case '1552':
        Dialog.create({
          title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-28" ></i>${response.data.message}</p>`,
          html: true,
          ok: {
            unelevated: true,
            label: '确认'
          }
        })
        break
      default:
        Dialog.create({
          title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-52" ></i> 提示</p>`,
          message: `<p class="word-break"> ${ErrCode[response.data.code]
            ? yzCode.includes(response.data.code) ? response.data.message ? response.data.message : ErrCode[response.data.code] : ErrCode[response.data.code]
            : '未知错误'}</p>`,
          html: true,
          ok: {
            unelevated: true,
            label: '确认'
          }
        })
        break
    }

    return Promise.reject(ErrCode[response.data.code])
  }, error => {
    if (error.response) {
      // let url = error.response.config.url
      switch (error.response.status) {
        case 404:
          // saveTokenOver(true)
          // router.push('/login/child')
          Dialog.create({
            title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-52" ></i> 访问接口失败</p>`,
            message: `没有找到接口:${error.response.data.path}`,
            html: true,
            ok: {
              unelevated: true,
              label: '确认'
            }
          })
          break
        case 500:
        case 501:
        case 502:
        case 503:
          Dialog.create({
            title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-52" ></i> 访问接口失败</p>`,
            message: `服务器出错,status错误码为${error.response.status}`,
            html: true,
            ok: {
              unelevated: true,
              label: '确认'
            }
          })
          break
        default:
          Dialog.create({
            title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-52" ></i> 访问接口失败</p>`,
            message: `访问出错`,
            html: true,
            ok: {
              unelevated: true,
              label: '确认'
            }
          })
          break
      }
    }
    return Promise.reject(error.response.data)
  }
)

export default async ({ Vue }) => {
  Vue.prototype.$axios = axiosInstance
}

export { axiosInstance }

/**
 * Token 续签锁
 * <p>
 * 续签 token 时上锁（true），续签结束释放锁（false）
 *
 * @type {boolean}
 */
let renewTokenLock = false

/**
 * 请求队列
 * <p>
 * 续签 token 的期间，所有发出的请求都阻塞在队列中，续签成功后会依序重放请求
 *
 * @type {{}[]}
 */
let pendingRequests = []

/**
 * 续签 access token
 */
function renewAccessToken () {
  // 若续签锁已被获取，则直接返回，避免重复续签 token
  if (renewTokenLock) {
    return
  }
  renewTokenLock = true

  if (!loadRefreshToken()) {
    doWhenRefreshTokenInvalid()
  }

  // 续签
  ;(async () => {
    let response
    let store = getStore()
    let tid = store.getters.userInfo.tenant_id
    try {
      response = await axiosInstance.post('/v1/refresh_token', undefined, {
        headers: { 'Authorization': 'Bearer ' + loadRefreshToken(), 'tid': tid }
      })
    } catch {
      doWhenRefreshTokenInvalid()
      return
    }

    if (response.code !== '1200') {
      doWhenRefreshTokenInvalid()
      return
    }

    // 存储新的 access token & refresh token
    saveToken(response.data.access_token)
    saveRefreshToken(response.data.refresh_token)

    renewTokenLock = false

    // 重放阻塞的请求
    executeAllPendingRequests()
  })()
}

/**
 * 当 refresh token 失效（过期 or 非法）时的操作
 */
function doWhenRefreshTokenInvalid () {
  pendingRequests = []
  renewTokenLock = false

  removeToken()
  removeRefreshToken()

  // router.push('/login')
  // setTimeout(() => {
  //   router.go(0)
  // }, 100)
  goLoginPage()
}

/**
 * 将 Axios 请求放入队列，在适当时机，队列中的所有请求将被依序执行
 *
 * @param {{}} reqConfig Axios 请求 config 对象
 * @return {Promise<{}>} 该 Axios 请求对应的 promise 对象，在请求执行成功后 resolve，失败后 reject
 */
function pendRequest (reqConfig) {
  let resolveFunc
  let rejectFunc
  const promise = new Promise((resolve, reject) => {
    resolveFunc = resolve
    rejectFunc = reject
  })

  pendingRequests.push({
    config: reqConfig,
    resolve: resolveFunc,
    reject: rejectFunc
  })

  return promise
}

/**
 * 重放请求的重试次数
 *
 * @type {number}
 */
let retryCounter = 0

/**
 * 重放请求的最大重试次数
 * <p>
 * 限制重放请求的重试次数，作为重放机制的后备保护，避免特殊情况下陷入重放死循环
 *
 * @type {number}
 */
const MAX_RETRY_COUNT = 3

/**
 * 依序执行 Axios 请求队列中的所有请求
 */
function executeAllPendingRequests () {
  // 限制最大重放次数
  retryCounter++
  if (retryCounter > MAX_RETRY_COUNT) {
    retryCounter = 0
    doWhenRefreshTokenInvalid()
  }

  pendingRequests.forEach(async req => {
    try {
      // 使用最新的 access token 替换旧的 Authorization 头
      const config = req.config
      if (config.hasOwnProperty('headers') && config.headers.hasOwnProperty('Authorization')) {
        config.headers.Authorization = getToken()
      }

      const response = await axiosInstance.request(config)
      req.resolve(response)

      retryCounter = 0
    } catch (e) {
      req.reject(e)

      retryCounter = 0
    }
  })

  pendingRequests = []
}
